/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.yonsei.iwb.sdt.refinement.generator;

import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.XSD;
import edu.yonsei.iwb.sdt.algorithm.DCSSearcher;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import edu.yonsei.iwb.sdt.datastructure.tree.BranchingType;
import edu.yonsei.iwb.sdt.datastructure.DecomposeCandidate;
import edu.yonsei.iwb.sdt.datastructure.DecomposeCandidateSet;
import edu.yonsei.iwb.sdt.datastructure.TargetInstance;
import edu.yonsei.iwb.sdt.ontology.PropertyArc;
import edu.yonsei.iwb.sdt.refinement.DLConstructors;
import edu.yonsei.iwb.sdt.refinement.DomainRestrictionRefinement;
import edu.yonsei.iwb.sdt.refinement.ExpandingHistory;
import edu.yonsei.iwb.sdt.refinement.ExpandingHistorySet;
import edu.yonsei.iwb.sdt.refinement.Refinement;
import edu.yonsei.iwb.sdt.refinement.RefinementType;
import edu.yonsei.iwb.sdt.refinement.ValueInfoOfDP;
import edu.yonsei.iwb.sdt.util.SDTLog;
import edu.yonsei.iwb.ts.TSSearch;
import edu.yonsei.iwb.ts.model.ObjectNode;
import java.io.IOException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 *
 * @author JDK
 */
public class DomainRestrictionRefinementMaker extends Thread {

    private String property;
    private Vector<TargetInstance> instanceSet;
    private int datatype;
    private Object standard;
    private ValueInfoOfDP vio;
    private Vector<PropertyArc> hisPAset;
    private DecomposeCandidateSet dcs;
    private QuantificationRefinementMaker qrm;

    public DomainRestrictionRefinementMaker(Vector<TargetInstance> instanceSet) {
        this.instanceSet = instanceSet;
        this.qrm = new QuantificationRefinementMaker(RefinementType.DOMAINRESTRICTION);
    }

    public DomainRestrictionRefinementMaker(String property, ValueInfoOfDP vio, Vector<TargetInstance> instanceSet, Vector<PropertyArc> propertyHistory, int datatype, DecomposeCandidateSet dcs) {
        this.property = property;
        this.instanceSet = instanceSet;
        this.hisPAset = propertyHistory;
        this.datatype = datatype;
        this.vio = vio;
        this.dcs = dcs;
        this.qrm = new QuantificationRefinementMaker(RefinementType.DOMAINRESTRICTION);
    }

    public DomainRestrictionRefinementMaker(String property, ValueInfoOfDP vio, Vector<TargetInstance> instanceSet, Vector<PropertyArc> propertyHistory, int datatype, Object standard, DecomposeCandidateSet dcs) {
        this.property = property;
        this.instanceSet = instanceSet;
        this.hisPAset = propertyHistory;
        this.datatype = datatype;
        this.vio = vio;
        this.dcs = dcs;
        this.standard = standard;
        this.qrm = new QuantificationRefinementMaker(RefinementType.DOMAINRESTRICTION);
    }

    @Override
    public synchronized void run() {
        try {
            long startTime = System.currentTimeMillis();
            SDTLog.log(SDTLog.DETAIL, "# Generating DRR : Property : " + property +" / "+standard+ " is generated...\n");
            boolean gen = decomposeCandidateOfDRR();
            DCSSearcher.FLAGNUM--;
            long endTime = System.currentTimeMillis();
            if (gen) {
                SDTLog.log(SDTLog.DETAIL, "# DRR : Property : " + property + " is generated..." + ((endTime - startTime) / 1000.0f) + "s)\n");
            } else {
                SDTLog.log(SDTLog.DETAIL, "# - DRR : Property : " + property + " is NOT generated..." + ((endTime - startTime) / 1000.0f) + "s)\n");
            }
        } catch (IOException ex) {
            Logger.getLogger(DomainRestrictionRefinementMaker.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ParseException ex) {
            Logger.getLogger(DomainRestrictionRefinementMaker.class.getName()).log(Level.SEVERE, null, ex);
        }

    }

    private boolean decomposeCandidateOfDRR() throws IOException, ParseException {
        if (hisPAset.isEmpty()) {
            Vector<Refinement> refset = null;
            if (datatype == ValueInfoOfDP.BOOLEAN) {
                refset = this.refinementGenerator("true", "==", "!=");
            } else if (datatype == ValueInfoOfDP.DATARANGE) {
                Vector standardset = vio.getStandard();
                refset = this.refinementGenerator(standardset);
            } else if (datatype == ValueInfoOfDP.INT) {
                refset = this.refinementGenerator(standard.toString(), ">", "<=");
//            } else if (datatype == ValueInfoOfDP.STRING) {
            } else if (datatype == ValueInfoOfDP.DATE) {
                refset = this.refinementGenerator(standard.toString(), ">", "<=");
//            } else if (datatype == ValueInfoOfDP.STRING) {
            } else {
                refset = this.refinementGenerator(standard.toString(), "==", "!=");
            }

            if (refset != null) {
                this.dcs.add(new DecomposeCandidate(refset, RefinementType.DOMAINRESTRICTION));
                return true;
            } else {
                return false;
            }
        } else {
            DecomposeCandidateSet newdcs = null;
            if (datatype == ValueInfoOfDP.BOOLEAN) {
                newdcs = this.refinementGenerator("true", "==", "!=", hisPAset);
            } else if (datatype == ValueInfoOfDP.DATARANGE) {
                Vector standardset = vio.getStandard();
                newdcs = this.refinementGenerator(standardset, hisPAset);                
            } else if (datatype == ValueInfoOfDP.INT) {
                newdcs = this.refinementGenerator(standard.toString(), ">", "<=", hisPAset);
//            } else if (datatype == ValueInfoOfDP.STRING) {
            } else if (datatype == ValueInfoOfDP.DATE) {
                newdcs = this.refinementGenerator(standard.toString(), ">", "<=", hisPAset);
//            } else if (datatype == ValueInfoOfDP.STRING) {
            }  else {
                newdcs = this.refinementGenerator(standard.toString(), "==", "!=", hisPAset);
            }

            if (newdcs != null) {
                this.dcs.addAll(newdcs);
                return true;
            } else {
                return false;
            }
        }
    }

    public Vector<Refinement> refinementGenerator(String standard, String true_prefix, String false_prefix) throws IOException, ParseException {

        Vector<Refinement> refset = new Vector<Refinement>();

//        ArrayList<String> res = TSSearch.listSubjectResource(property);
        Vector<String> css = this.getSatisfiedInstanceSet(standard);

        Vector<TargetInstance> cloneSet = new Vector(instanceSet);
        Vector<TargetInstance> sfTIset = new Vector();
        for (int j = 0; j < css.size(); j++) {
            String result = css.get(j);
            for (int k = 0; k < this.instanceSet.size(); k++) {
                TargetInstance ti = this.instanceSet.get(k);
                if (ti.getUri().equals(result)) {
                    sfTIset.add(ti);
                    cloneSet.remove(ti);
                }
            }
        }

        if (!sfTIset.isEmpty()) {
            Refinement pref = new DomainRestrictionRefinement(standard, true_prefix, property,vio.getType());
            pref.setInstanceSet(sfTIset);
            refset.add(pref);
        }

        if (!cloneSet.isEmpty()) {
            Refinement nref = new DomainRestrictionRefinement(standard, false_prefix, property,vio.getType());
            nref.setBranchingType(BranchingType.NEGATION_TYPE);
            nref.setInstanceSet(cloneSet);
            refset.add(nref);
        }

        return refset;
    }

    public Vector<Refinement> refinementGenerator(Vector standardset) throws IOException, ParseException {
        //owl:datarange case
        Vector<Refinement> refset = new Vector<Refinement>();

//        ArrayList<String> res = TSSearch.listSubjectResource(property);
        Vector<TargetInstance> cloneSet = new Vector(instanceSet);

        for (int i = 0; i < standardset.size(); i++) {
            String st = standardset.get(i).toString();
            Vector<String> css = this.getSatisfiedInstanceSet(st);
            Vector<TargetInstance> sfTIset = new Vector();
            for (int j = 0; j < css.size(); j++) {
                String result = css.get(j);
                for (int k = 0; k < this.instanceSet.size(); k++) {
                    TargetInstance ti = this.instanceSet.get(k);
                    if (ti.getUri().equals(result)) {
                        sfTIset.add(ti);
                        cloneSet.remove(ti);
                    }
                }
            }
            if (!sfTIset.isEmpty()) {
                Refinement pref = new DomainRestrictionRefinement(st, "==", property,vio.getType());
                refset.add(pref);
            }
        }

        if (!cloneSet.isEmpty()) {
            Refinement nref = new DomainRestrictionRefinement(standardset, property);
            nref.setInstanceSet(cloneSet);
            nref.setBranchingType(BranchingType.NEGATION_TYPE);
            refset.add(nref);
        }

        return refset;
    }

    public DecomposeCandidateSet refinementGenerator(String standard, String true_prefix, String false_prefix, Vector<PropertyArc> propertyHistory) throws IOException, ParseException {
        //property set exist 
//        ArrayList<String> res = TSSearch.listSubjectResource(property);
        Vector<String> css = this.getSatisfiedInstanceSet(standard);

        ExpandingHistorySet ehs = this.qrm.expandingHistoryCounter(propertyHistory, css);
        DecomposeCandidateSet dcs = new DecomposeCandidateSet();

        for (int i = 0; i < ehs.size(); i++) {
            ExpandingHistory eh = ehs.get(i);
            Vector<Refinement> refset = new Vector<Refinement>();

            Vector<String> is = eh.getInstanceSet();
            Vector<TargetInstance> cloneSet = new Vector(instanceSet);
            Vector<TargetInstance> sfTIset = new Vector();

            for (int j = 0; j < is.size(); j++) {
                String result = is.get(j);
                for (int k = 0; k < this.instanceSet.size(); k++) {
                    TargetInstance ti = this.instanceSet.get(k);
                    if (ti.getUri().equals(result)) {
                        sfTIset.add(ti);
                        cloneSet.remove(ti);
                    }
                }
            }

            if (!sfTIset.isEmpty()) {
                Refinement pref = new DomainRestrictionRefinement(standard, true_prefix, eh, property, vio.getType());
                pref.setInstanceSet(sfTIset);
                refset.add(pref);
            }

            if (!cloneSet.isEmpty()) {
                Refinement nref = new DomainRestrictionRefinement(standard, false_prefix, eh, property,vio.getType());
                nref.setBranchingType(BranchingType.NEGATION_TYPE);
                nref.setInstanceSet(cloneSet);
                refset.add(nref);
            }

            if (!refset.isEmpty()) {
                DecomposeCandidate dc = new DecomposeCandidate(refset, RefinementType.DOMAINRESTRICTION);
                dcs.add(dc);
            }
        }

        return dcs;
    }

    public DecomposeCandidateSet refinementGenerator(Vector standardset, Vector<PropertyArc> propertyHistory) throws IOException, ParseException {
        //property set exist and owl:datarange case

//        ArrayList<String> res = TSSearch.listSubjectResource(property);
//        Vector<TargetInstance> cloneSet = new Vector(instanceSet);


        DecomposeCandidateSet dcs = new DecomposeCandidateSet();

        Hashtable<Integer, ExpandingHistorySet> ehlist = new Hashtable<Integer, ExpandingHistorySet>();

        for (int i = 0; i < standardset.size(); i++) {
            String st = standardset.get(i).toString();
            Vector<String> css = this.getSatisfiedInstanceSet(st);
            ExpandingHistorySet ehs = this.qrm.expandingHistoryCounter(propertyHistory, css);
            for (int j = 0; j < ehs.size(); j++) {
                ExpandingHistory eh = ehs.get(j);
                this.putEH(ehlist, j, eh);
            }
        }

        Enumeration<Integer> e = ehlist.keys();
        while (e.hasMoreElements()) {
            int key = e.nextElement();
            ExpandingHistorySet ehs = ehlist.get(key);
            Vector<Refinement> refset = new Vector<Refinement>();
            Vector<TargetInstance> cloneSet = new Vector(instanceSet);

            for (int i = 0; i < ehs.size(); i++) {
                ExpandingHistory eh = ehs.get(i);
//                Refinement pref = new DomainRestrictionRefinement("", standardset.get(i).toString(), "==", eh, property);

                Vector<String> is = eh.getInstanceSet();
                Vector<TargetInstance> sfTIset = new Vector();

                for (int j = 0; j < is.size(); j++) {
                    String result = is.get(j);
                    for (int k = 0; k < this.instanceSet.size(); k++) {
                        TargetInstance ti = this.instanceSet.get(k);
                        if (ti.getUri().equals(result)) {
                            sfTIset.add(ti);
                            cloneSet.remove(ti);
                        }
                    }
                }

                if (!sfTIset.isEmpty()) {
                    Refinement pref = new DomainRestrictionRefinement(standardset.get(i).toString(), "==", eh, property,vio.getType());
                    pref.setInstanceSet(sfTIset);
                    refset.add(pref);
                }


            }
            if (!cloneSet.isEmpty()) {
                Refinement nref = new DomainRestrictionRefinement(standardset, ehs.firstElement(), property);
                nref.setBranchingType(BranchingType.NEGATION_TYPE);
                nref.setInstanceSet(cloneSet);
                refset.add(nref);
            }

            if (!refset.isEmpty()) {
                DecomposeCandidate dc = new DecomposeCandidate(refset, RefinementType.DOMAINRESTRICTION);
                dcs.add(dc);
            }
//            dcs.printDecomposeCandidateSet();
        }
        return dcs;
    }

    private void putEH(Hashtable<Integer, ExpandingHistorySet> ehlist, int key, ExpandingHistory eh) {
        if (ehlist.keySet().contains(key)) {
            ehlist.get(key).add(eh);
        } else {
            ExpandingHistorySet ehs = new ExpandingHistorySet();
            ehs.add(eh);
            ehlist.put(key, ehs);
        }
    }

    private String getHeadPrefix() {
        String s = new String();
        if (datatype == ValueInfoOfDP.INT) {
            s = "";
        } else {
            s = DLConstructors.NEGATION;
        }

        return s;
    }

    private String getPrefix(Resource datatype, boolean value) {
        String s = new String();

        if (datatype.equals(XSD.xint)) {
            if (value) {
                s = ">";
            } else {
                s = "<=";
            }
        } else {
            s = "==";
        }

        return s;
    }
    
    private Vector<String> getSatisfiedInstanceSet(String standard) throws IOException, ParseException {
        Vector<String> css = new Vector();
        SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
               
        Hashtable<String, ArrayList<String>> randvt = this.vio.getRandValueTable();        
        Enumeration<String> resources = randvt.keys();
        while(resources.hasMoreElements()){
            String subject = resources.nextElement();
            ArrayList<String> valueset = randvt.get(subject);
            
            if (datatype == ValueInfoOfDP.BOOLEAN || datatype == ValueInfoOfDP.STRING) {
                for (int j = 0; j < valueset.size(); j++) {                    
                    String value = valueset.get(j);
                    if (value.equals(standard)) {
                        css.add(subject);
                        break;
                    }
                }
            } else if (datatype == ValueInfoOfDP.INT) {
                int stan = Integer.parseInt(standard);
                for (int j = 0; j < valueset.size(); j++) {
                    String value = valueset.get(j);
                    int intv = Integer.parseInt(value);
                    if (intv > stan) {
                        css.add(subject);
                        break;
                    }
                }
            } else if (datatype == ValueInfoOfDP.DATARANGE) {
                for (int j = 0; j < valueset.size(); j++) {
                    String value = valueset.get(j);                    
                    if (value.equals(standard)) {
                        css.add(subject);
                        break;
                    }
                }
            }  else if (datatype == ValueInfoOfDP.DATE) {
                Date sDate = formatter.parse(standard);
                for (int j = 0; j < valueset.size(); j++) {                    
                    String value = valueset.get(j);
                    Date vDate = formatter.parse(value);
                    System.out.println(vDate + " ? " + sDate);
                    if (vDate.after(sDate)) {
                        System.out.println(vDate + " > " + sDate);
                        css.add(subject);
                        break;
                    }
                }
            }
        }
        return css;
    }

//    private Vector<String> getSatisfiedInstanceSet(String standard, ArrayList<String> res) throws IOException {
//        Vector<String> css = new Vector();
//
//        for (int i = 0; i < res.size(); i++) {
//            String r = res.get(i);
//
//            ArrayList<ObjectNode> onl = TSSearch.listObjectNode(r, property);
//            if (datatype == ValueInfoOfDP.BOOLEAN || datatype == ValueInfoOfDP.STRING) {
//                for (int j = 0; j < onl.size(); j++) {
//                    ObjectNode on = onl.get(j);
//                    String value = on.getValue();
//                    if (value.equals(standard)) {
//                        css.add(r);
//                        break;
//                    }
//                }
//            } else if (datatype == ValueInfoOfDP.INT) {
//                int stan = Integer.parseInt(standard);
//                for (int j = 0; j < onl.size(); j++) {
//                    ObjectNode on = onl.get(j);
//                    int value = Integer.parseInt(on.getValue());
//                    if (value > stan) {
//                        css.add(r);
//                        break;
//                    }
//                }
//            } else if (datatype == ValueInfoOfDP.DATARANGE) {
//                for (int j = 0; j < onl.size(); j++) {
//                    ObjectNode on = onl.get(j);
//                    String value = on.getValue();
//                    if (value.equals(standard)) {
//                        css.add(r);
//                        break;
//                    }
//                }
//            }
//        }
//
//        return css;
//    }
}
